using System;
using System.Linq;
using System.Reflection;
using MetaObjects.Contracts;

namespace MetaObjects.Core
{
	public class ObjectProvider<TEntity> : IMetaEntityProvider<Type>, IMetaFieldProvider<PropertyInfo>
	{
		public Func<PropertyInfo, IMetaField> FieldFactory { get; set; }
		Func<object, IMetaField> IMetaFieldProvider.FieldFactory
		{
			get { return f => FieldFactory((PropertyInfo)f); }
		}

		public Func<Type, IMetaEntity> EntityFactory { get; set; }
		Func<object, IMetaEntity> IMetaEntityProvider.EntityFactory
		{
			get { return e => EntityFactory((Type)e); }
		}

		public ObjectProvider()
		{
			EntityFactory = GetMetaEntity;
			FieldFactory = GetMetaField;
		}

		public IMetaEntity<TEntity> GetMetaEntity(Func<TEntity> factory)
		{
			var entity = typeof(TEntity);
			var fields = entity.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Select(f => GetMetaField(f));
			MetaEntity<TEntity> metaEntity = new MetaEntity<TEntity>(entity.Name, factory, fields);
			return metaEntity;
		}

		public IMetaEntity GetMetaEntity(Type entity)
		{
			var fields = entity.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Select(FieldFactory);
			MetaEntity<object> metaEntity = new MetaEntity<object>(entity.Name, () => Activator.CreateInstance(entity), fields);
			return metaEntity;
		}

		//public IMetaField GetMetaField(PropertyInfo field)
		//{
		//    MetaField metaField = new MetaField(field.Name)
		//    {
		//        DisplayName = field.Name,
		//        ReadOnly = !field.CanWrite,
		//        GetField = entity => field.GetValue(entity, new object[0]),
		//        SetField = (entity, value) => field.SetValue(entity, value, new object[0]),
		//        DefaultValue = field.PropertyType.IsValueType ? Activator.CreateInstance(field.PropertyType) : null,
		//    };
		//    return metaField;
		//}

		public IMetaField GetMetaField(PropertyInfo field)
		{
			MetaField metaField = new MetaField(field.Name)
			{
				DisplayName = field.Name,
				ReadOnly = !field.CanWrite,
				GetField = entity => PropertyAccessor<TEntity>.GetPropertyDelegate(field.Name)((TEntity)entity),
				SetField = (entity, value) => PropertyAccessor<TEntity>.SetPropertyDelegate(field.Name)((TEntity)entity, value),
				DefaultValue = field.PropertyType.IsValueType ? Activator.CreateInstance(field.PropertyType) : null,
			};
			return metaField;
		}
	}
}